<template>
  <div
    class="AircraftBattle"
    :style="{ width: APP_WIDTH + 'px', height: APP_HEIGHT + 'px' }"
  >
    <!-- 待开始 -->
    <template v-if="useAppStore.status === 'begin'">
      <img
        src="@/image/startgame.png"
        title="点击开始"
        @click="handleStartGame"
      />
    </template>
    <!-- 进行中/结束 -->
    <template v-if="useAppStore.status !== 'begin'">
      <img src="@/image/bg.png" />
      <!-- 分数 -->
      <span class="fraction">分数：{{ useAppStore.fraction }}</span>
      <!-- 杀敌数 -->
      <span class="kills">杀敌数：{{ useAppStore.kills }}</span>
      <!-- 暂停 -->
      <img
        v-if="!useAppStore.isPause && useAppStore.status === 'progress'"
        src="@/image/pause.png"
        class="pause"
        title="点击暂停"
        @click="handlePause"
      />
      <!-- 继续 -->
      <div
        v-if="useAppStore.isPause && useAppStore.status === 'progress'"
        class="go"
        @click="handleGo"
        title="点击继续"
      ></div>
      <!-- 敌机 -->
      <viewAircraft
        v-for="item in useAppStore.enemyList"
        :info="item"
      ></viewAircraft>
      <!-- 玩家子弹 -->
      <img
        class="bullet"
        v-for="item in useAppStore.bulletList"
        :style="{ top: item.y + 'px', left: item.x + 'px' }"
        src="@/image/bullet.png"
        :width="BULLET_WIDTH"
        :height="BULLET_HEIGHT"
      />
      <!-- 玩家 -->
      <viewAircraft
        :info="useAppStore.player"
        v-if="useAppStore.player.blood > 0"
      ></viewAircraft>
      <!-- 游戏结束 -->
      <div
        class="over"
        v-if="useAppStore.status === 'over'"
        title="点击重新开始"
        @click="handleStartGame"
      >
        游戏结束
      </div>
    </template>
  </div>
</template>
<script setup lang="ts">
import type { Aircraft } from "@/type";
import hxzxUtils from "hxzx-utils";
import viewAircraft from "@/components/aircraft.vue";
import store from "@/store";
import aircraft from "@/components/aircraft";
const useAppStore = store.useAppStore();
/**
 * 玩家基础信息
 */
const playerInfo = aircraft["player"];
/**
 * 容器宽度
 */
const APP_WIDTH = 320;
/**
 * 容器高度
 */
const APP_HEIGHT = 568;
/**
 * 创建敌机频率
 */
const CREATE_ENEMY_RATE = 1000;
/**
 * 敌机速度
 */
const CREATE_ENEMY_SPEED = 20;
/**
 * 玩家子弹宽度
 */
const BULLET_WIDTH = 6;
/**
 * 玩家子弹高度
 */
const BULLET_HEIGHT = 14;
/**
 * 玩家子弹发射频率
 */
const BULLET_RATE = 400;
/**
 * 玩家子弹速度
 */
const BULLET_SPEED = 200;
/**
 * 玩家攻击
 */
const PLAYER_ATTACK = 1;
/**
 * 玩家移动速度
 */
const PLAYER_MOVE_SPEED = 200;
/**
 * 视图上最多敌机数量
 */
const MAX_ENEMY_COUNT = 30;
/**
 * @description: 判断两个元素是否碰撞
 * @param {number} x1 第一个元素x坐标
 * @param {number} y1 第一个元素y坐标
 * @param {number} w1 第一个元素宽度
 * @param {number} h1 第一个元素高度
 * @param {number} x2 第二个元素x坐标
 * @param {number} y2 第二个元素y坐标
 * @param {number} w2 第二个元素宽度
 * @param {number} h2 第二个元素高度
 * @return {boolean}
 */
const isCollision = function (
  x1: number,
  y1: number,
  w1: number,
  h1: number,
  x2: number,
  y2: number,
  w2: number,
  h2: number
): boolean {
  const range1 = [
    [x1, y1],
    [x1 + w1, y1 + h1],
  ];
  const range2 = [
    [x2, y2],
    [x2 + w2, y2 + h2],
  ];
  x1 = range1[0][0];
  y1 = range1[0][1];
  x2 = range1[1][0];
  y2 = range1[1][1];
  const x3 = range2[0][0];
  const y3 = range2[0][1];
  const x4 = range2[1][0];
  const y4 = range2[1][1];

  return !(x1 >= x4 || x2 <= x3 || y1 >= y4 || y2 <= y3);
};
/**
 * 生产敌机定时器
 */
let enemyCreateTimer: NodeJS.Timeout;
/**
 * 创建敌机
 */
const createEnemy = function () {
  clearTimeout(enemyCreateTimer);
  if (useAppStore.enemyList.length < MAX_ENEMY_COUNT) {
    const info: Aircraft = {
      id: hxzxUtils.random.id(),
      type: "enemy1",
      x: -1,
      y: -1,
      blood: -1,
      status: true,
    };
    const typeList: Aircraft["type"][] = [];
    if (useAppStore.kills <= 5) {
      typeList.push("enemy1");
    } else if (useAppStore.kills <= 20) {
      typeList.push("enemy1", "enemy2");
    } else {
      typeList.push("enemy1", "enemy2", "enemy3");
    }
    info.type = typeList[hxzxUtils.random.number(1, typeList.length) - 1];
    const data = aircraft[info.type];
    info.blood = data.blood;
    info.y = 0 - data.h;
    info.x = hxzxUtils.random.number(0, APP_WIDTH - data.w);
    useAppStore.enemyList.push(info);
  }
  enemyCreateTimer = setTimeout(createEnemy, CREATE_ENEMY_RATE);
};
/**
 * 敌机移动定时器
 */
let enemyMoveTimer: NodeJS.Timeout;
/**
 * 敌机移动
 */
const enemyMove = function () {
  clearTimeout(enemyMoveTimer);
  useAppStore.enemyList.forEach(function (item) {
    if (item.status) {
      item.y += 1;
    }
  });
  enemyMoveTimer = setTimeout(enemyMove, 1000 / CREATE_ENEMY_SPEED);
};
/**
 * 创建子弹定时器
 */
let bulletTimer: NodeJS.Timeout;
/**
 * 创建子弹
 */
const createBullet = function () {
  clearTimeout(bulletTimer);
  useAppStore.bulletList.push({
    id: hxzxUtils.random.id(),
    x: useAppStore.player.x + (playerInfo.w - BULLET_WIDTH) / 2,
    y: useAppStore.player.y - BULLET_HEIGHT,
  });
  bulletTimer = setTimeout(createBullet, BULLET_RATE);
};
/**
 * 子弹移动定时器
 */
let bulletMoveTimer: NodeJS.Timeout;
/**
 * 子弹移动
 */
const bulletMove = function () {
  clearTimeout(bulletMoveTimer);
  useAppStore.bulletList.forEach(function (item) {
    item.y -= 1;
  });
  bulletMoveTimer = setTimeout(bulletMove, 1000 / BULLET_SPEED);
};
/**
 * 玩家移动
 */
let isDown = false;
let playerDirectionLeft = false;
let playerDirectionRight = false;
let playerDirectionTop = false;
let playerDirectionBottom = false;
let playerMoveTimer: NodeJS.Timeout;
const playerMove = function () {
  clearTimeout(playerMoveTimer);
  if (
    playerDirectionLeft ||
    playerDirectionRight ||
    playerDirectionTop ||
    playerDirectionBottom
  ) {
    playerMoveTimer = setTimeout(function () {
      if (playerDirectionLeft) {
        useAppStore.player.x = Math.max(0, useAppStore.player.x - 1);
      } else if (playerDirectionRight) {
        useAppStore.player.x = Math.min(
          APP_WIDTH - playerInfo.w,
          useAppStore.player.x + 1
        );
      }

      if (playerDirectionTop) {
        useAppStore.player.y = Math.max(0, useAppStore.player.y - 1);
      } else if (playerDirectionBottom) {
        useAppStore.player.y = Math.min(
          APP_HEIGHT - playerInfo.h,
          useAppStore.player.y + 1
        );
      }
      playerMove();
    }, 1000 / PLAYER_MOVE_SPEED);
  }
};
document.addEventListener("keydown", function (event: KeyboardEvent) {
  if (useAppStore.status !== "progress") return;
  if (useAppStore.isPause) return;
  if (isDown) return;
  isDown = true;
  if (event.key === "a") {
    playerDirectionLeft = true;
    playerDirectionRight = false;
  }
  if (event.key === "d") {
    playerDirectionLeft = false;
    playerDirectionRight = true;
  }
  if (event.key === "w") {
    playerDirectionTop = true;
    playerDirectionBottom = false;
  }
  if (event.key === "s") {
    playerDirectionTop = false;
    playerDirectionBottom = true;
  }
  playerMove();
});
document.addEventListener("keyup", function (event: KeyboardEvent) {
  isDown = false;
  if (event.key === "a") {
    playerDirectionLeft = false;
  }
  if (event.key === "d") {
    playerDirectionRight = false;
  }
  if (event.key === "w") {
    playerDirectionTop = false;
  }
  if (event.key === "s") {
    playerDirectionBottom = false;
  }
  playerMove();
});
/**
 * 停止所有计时器
 */
const stopAllTimer = function () {
  clearTimeout(enemyCreateTimer);
  clearTimeout(enemyMoveTimer);
  clearTimeout(bulletTimer);
  clearTimeout(bulletMoveTimer);
  clearTimeout(playerMoveTimer);
};
/**
 * 侦听器
 */
const listen = function () {
  if (useAppStore.status === "progress") {
    const ids: string[] = [];
    // 子弹 - 越界
    useAppStore.bulletList.forEach(function (item) {
      if (item.y < 0 - BULLET_HEIGHT) {
        ids.push(item.id);
      }
    });
    ids.forEach(function (id) {
      const index = useAppStore.bulletList.findIndex((e) => e.id === id);
      if (index >= 0) {
        useAppStore.bulletList.splice(index, 1);
      }
    });
    ids.splice(0, ids.length);
    // 敌机 - 越界
    useAppStore.enemyList.forEach(function (item) {
      if (item.y > APP_HEIGHT) {
        ids.push(item.id);
      }
    });
    ids.forEach(function (id) {
      const index = useAppStore.enemyList.findIndex((e) => e.id === id);
      if (index >= 0) {
        useAppStore.enemyList.splice(index, 1);
      }
    });
    // 敌机与玩家碰撞
    for (let i = 0; i < useAppStore.enemyList.length; i++) {
      const item = useAppStore.enemyList[i];
      if (
        item.status &&
        isCollision(
          item.x,
          item.y,
          aircraft[item.type].w,
          aircraft[item.type].h,
          useAppStore.player.x,
          useAppStore.player.y,
          playerInfo.w,
          playerInfo.h
        )
      ) {
        useAppStore.player.status = false;
        stopAllTimer();
        setTimeout(function () {
          useAppStore.status = "over";
          useAppStore.player.blood = 0;
        }, 500);
        return;
      }
    }
    // 子弹与敌机碰撞
    ids.splice(0, ids.length);
    const enemyIds: string[] = [];
    for (let j = 0; j < useAppStore.bulletList.length; j++) {
      const bullet = useAppStore.bulletList[j];
      for (let i = 0; i < useAppStore.enemyList.length; i++) {
        const enemy = useAppStore.enemyList[i];
        if (
          enemy.status &&
          isCollision(
            bullet.x,
            bullet.y,
            BULLET_WIDTH,
            BULLET_HEIGHT,
            enemy.x,
            enemy.y,
            aircraft[enemy.type].w,
            aircraft[enemy.type].h
          )
        ) {
          ids.push(bullet.id);
          enemy.blood -=
            PLAYER_ATTACK + Math.min(0.6, useAppStore.fraction / 100);
          if (enemy.blood <= 0) {
            useAppStore.kills += 1;
            useAppStore.fraction += aircraft[enemy.type].blood;
            enemy.status = false;
            enemyIds.push(enemy.id);
          }
        }
      }
    }
    ids.forEach(function (id) {
      const index = useAppStore.bulletList.findIndex((e) => e.id === id);
      if (index >= 0) {
        useAppStore.bulletList.splice(index, 1);
      }
    });
    setTimeout(function () {
      enemyIds.forEach(function (id) {
        const index = useAppStore.enemyList.findIndex((e) => e.id === id);
        if (index >= 0) {
          useAppStore.enemyList.splice(index, 1);
        }
      });
    }, 500);
  }
  requestAnimationFrame(listen);
};
listen();

/**
 * @description: 开始游戏
 * @return {*}
 */
const handleStartGame = function (): void {
  useAppStore.player.status = true;
  useAppStore.player.blood = 1;
  useAppStore.player.x = (APP_WIDTH - playerInfo.w) / 2;
  useAppStore.player.y = APP_HEIGHT - playerInfo.h;
  useAppStore.kills = 0;
  useAppStore.enemyList.splice(0, useAppStore.enemyList.length);
  stopAllTimer();
  createEnemy();
  enemyMove();
  createBullet();
  bulletMove();
  useAppStore.status = "progress";
};
/**
 * @description: 暂停游戏
 * @return {*}
 */
const handlePause = function (): void {
  stopAllTimer();
  useAppStore.isPause = true;
};
/**
 * @description: 继续游戏
 * @return {*}
 */
const handleGo = function (): void {
  createEnemy();
  enemyMove();
  createBullet();
  bulletMove();
  useAppStore.isPause = false;
};
</script>
<style lang="scss">
* {
  margin: 0;
  padding: 0;
  user-select: none;
  -webkit-user-drag: none;
}
.AircraftBattle {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
  .fraction {
    position: absolute;
    top: 4px;
    left: 4px;
    font-size: 14px;
    color: black;
    font-weight: bold;
    line-height: 1;
  }
  .kills {
    position: absolute;
    top: 22px;
    left: 4px;
    font-size: 14px;
    color: black;
    font-weight: bold;
    line-height: 1;
  }
  .pause {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 20px;
    height: 20px;
    cursor: pointer;
  }
  .go {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    cursor: pointer;
    border: 20px solid transparent;
    border-left-color: #9ca0a1;
  }
  .bullet {
    position: absolute;
  }
  .over {
    position: absolute;
    inset: 0;
    font-weight: bold;
    font-size: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
}
</style>
